package compiler;


import java.util.Iterator;

import Parser.syntaxtree.AdditiveExpression;
import Parser.syntaxtree.Assign;
import Parser.syntaxtree.Blok;
import Parser.syntaxtree.Call;
import Parser.syntaxtree.Definice;
import Parser.syntaxtree.DefiniceParametru;
import Parser.syntaxtree.DefinicePromenny;
import Parser.syntaxtree.DefinicePromennych;
import Parser.syntaxtree.Expression;
import Parser.syntaxtree.For;
import Parser.syntaxtree.Function;

import Parser.syntaxtree.HlavickaFunkce;
import Parser.syntaxtree.HlavickaProcedury;
import Parser.syntaxtree.INode;
import Parser.syntaxtree.Identifier;
import Parser.syntaxtree.IdentifierPrikaz;
import Parser.syntaxtree.If;
import Parser.syntaxtree.MultiplicativeExpression;
import Parser.syntaxtree.MyInteger;
import Parser.syntaxtree.NazevFunkce;
import Parser.syntaxtree.NazevProcedury;
import Parser.syntaxtree.NazevTypu;
import Parser.syntaxtree.NodeChoice;
import Parser.syntaxtree.NodeList;
import Parser.syntaxtree.NodeListOptional;
import Parser.syntaxtree.NodeOptional;
import Parser.syntaxtree.NodeSequence;
import Parser.syntaxtree.NodeToken;
import Parser.syntaxtree.Parametry;
import Parser.syntaxtree.Podminka;
import Parser.syntaxtree.Prikaz;
import Parser.syntaxtree.Procedure;
import Parser.syntaxtree.Program;
import Parser.syntaxtree.Return_st;
import Parser.syntaxtree.SeznamParametru;
import Parser.syntaxtree.Start;
import Parser.syntaxtree.UnaryExpression;
import Parser.syntaxtree.While;
import Parser.visitor.DepthFirstVoidVisitor;



public class Compiler extends DepthFirstVoidVisitor{
	  public ProcedureManager procedures = new ProcedureManager();
	  public String actProcedure;
	  public String identifierPrikazPreload =  null; //Kvuli charakteru gramatiky je nutny si tu obcas nechat ulozeny nejaky identifikator
//	  public VariableManager prepareToCall; //To samy, sem se budou postupne pridavat parametry predtim, nez se zavola nejaka funkce 
	  
	  public void visit(final NodeChoice n) {
	    n.choice.accept(this);
	    return;
	  }

	  public void visit(final NodeList n) {
	    for (final Iterator<INode> e = n.elements(); e.hasNext();) {
	      e.next().accept(this);
	    }
	    return;
	  }

	  public void visit(final NodeListOptional n) {
	    if (n.present()) {
	      for (final Iterator<INode> e = n.elements(); e.hasNext();) {
	        e.next().accept(this);
	        }
	      return;
	    } else
	      return;
	  }

	  public void visit(final NodeOptional n) {
	    if (n.present()) {
	      n.node.accept(this);
	      return;
	    } else
	    return;
	  }

	  public void visit(final NodeSequence n) {
	    for (final Iterator<INode> e = n.elements(); e.hasNext();) {
	      e.next().accept(this);
	    }
	    return;
	  }

	  public void visit(final NodeToken n) {
	    @SuppressWarnings("unused")
	    final String tkIm = n.tokenImage;
	    return;
	  }

	  public void visit(final Start n) {
	    n.f0.accept(this);
	  }

	  public void visit(final Program n) {
	    n.f0.accept(this);
	    n.f1.accept(this);
	  }

	  public void visit(final Procedure n) {
	    n.f0.accept(this);//hlavicka
	    n.f1.accept(this);//definice, nepovinna
	    n.f2.accept(this);//blok
	  }

	  public void visit(final Function n) {
	    n.f0.accept(this);
	    n.f1.accept(this);
	    n.f2.accept(this);
	  }

	  public void visit(final HlavickaProcedury n) {
	    n.f0.accept(this);// "procedure"
	    n.f1.accept(this);// nazev procedury. Visitor ho nastavi tam	      
	    n.f2.accept(this);// (
	    n.f3.accept(this);// nepovinne parametry. Visitor je nastavi tam
	    n.f4.accept(this);// )
	    n.f5.accept(this);// ;
	  }

	  public void visit(final HlavickaFunkce n) {
	    n.f0.accept(this);
	    n.f1.accept(this);
	    n.f2.accept(this);
	    n.f3.accept(this);
	    n.f4.accept(this);
	    n.f5.accept(this);
	    n.f6.accept(this);
	    n.f7.accept(this);
	  }

	  public void visit(final NazevFunkce n) {
	    n.f0.accept(this);
	  }

	  public void visit(final NazevProcedury n) {
          //n.f0.accept(this);
		  //Uz tam neni moc co navstevovat
          actProcedure = n.f0.tokenImage;
          procedures.declareProcedure( actProcedure );
          
	  }

	  public void visit(final SeznamParametru n) {
	    n.f0.accept(this);
	    n.f1.accept(this);
	  }

	  public void visit(final DefiniceParametru n) {
      //  n.f0.accept(this); //Tohle je nazev promenny
	  //  n.f1.accept(this); //Tohle je dvojtecka
	  //  n.f2.accept(this); //Tohle je typ promenny
	    procedures.addParameter( actProcedure, n.f0.f0.tokenImage, n.f2.f0.tokenImage ); //Ono je to jemno zaobaleny jeste v Identifieru, takze proto
	    
	  }

	  public void visit(final Definice n) {
	    n.f0.accept(this); //Sezer Var
	    n.f1.accept(this); //Definice promennych
	  }

	  public void visit(final DefinicePromennych n) {
	    n.f0.accept(this);//NodeList s DefinicemaPromenny
	  }

	  public void visit(final DefinicePromenny n) {
	    //n.f0.accept(this); // Nazev promenny
	    //n.f1.accept(this); // :
	    //n.f2.accept(this); // Typ promenny
	    //n.f3.accept(this); // ;
	    ProcedureC proc = procedures.getProcedureByName( actProcedure );
	    VariableManager vm = proc.variableManager;	    
	    vm.declareVariableByString( n.f0.f0.tokenImage, n.f2.f0.tokenImage );
	  }

	  public void visit(final NazevTypu n) {
	    n.f0.accept(this);
	  }

	  public void visit(final Blok n) {
	    n.f0.accept(this);// Begin
	    n.f1.accept(this);// List Prikazu
	    n.f2.accept(this);// End
	  }

	  public void visit(final Prikaz n) {
	    n.f0.accept(this); //Nektery z prikazu
	  }

	  public void visit(final Return_st n) {
	    n.f0.accept(this);
	    n.f1.accept(this);
	  }

	  public void visit(final For n) {
	    n.f0.accept(this);
	    n.f1.accept(this);
	    n.f2.accept(this);
	    n.f3.accept(this);
	    n.f4.accept(this);
	    n.f5.accept(this);
	    n.f6.accept(this);
	    n.f7.accept(this);
	    n.f8.accept(this);
	    n.f9.accept(this);
	    n.f10.accept(this);
	  }

	  public void visit(final While n) {
	    n.f0.accept(this);
	    n.f1.accept(this);
	    n.f2.accept(this);
	    n.f3.accept(this);
	    n.f4.accept(this);
	    n.f5.accept(this);
	  }

	  public void visit(final If n) {
	    n.f0.accept(this);
	    n.f1.accept(this);
	    n.f2.accept(this);
	    n.f3.accept(this);
	    n.f4.accept(this);
	    n.f5.accept(this);
	    n.f6.accept(this);
	  }

	  public void visit(final Assign n) {
		  //System.out.println("Assign");
	    n.f0.accept(this);//=
	    n.f1.accept(this);//Expression
	   
	  }

	  public void visit(final Call n) {
	    n.f0.accept(this);// (
	    n.f1.accept(this);// Parametry
	    n.f2.accept(this);// )
	  }

	  public void visit(final Podminka n) {
	    n.f0.accept(this);
	    n.f1.accept(this);
	    n.f2.accept(this);
	  }

	  public void visit(final Parametry n) {
	    n.f0.accept(this);//Expression
	    n.f1.accept(this);// ", Expression", o to se zatim starat nemusime, to sem postupne dobehne

	  }

	  public void visit(final Expression n) {
	    n.f0.accept(this);
	  }

	  public void visit(final AdditiveExpression n) {
	    n.f0.accept(this);
	    n.f1.accept(this);
	  }

	  public void visit(final MultiplicativeExpression n) {
	    n.f0.accept(this);
	    n.f1.accept(this);
	  }

	  public void visit(final UnaryExpression n) {
	    n.f0.accept(this);
	  }

	  public void visit(final Identifier n) {
	    n.f0.accept(this);
	    n.f1.accept(this);
	  }
	  
	  public void visit(final IdentifierPrikaz n) {
//		    n.f0.accept(this);
		  //V tehle funkci se nacte jmeno promenny, do ktery se ulozi nasledujici ASSIGN
		  //Nebo se pouze vyhodnoti jako expression a pripadne zavolaji funkce, ktery v sobe ma - CALL
		    identifierPrikazPreload = n.f0.f0.tokenImage;//Dalsi token bude Prikaz Call nebo Asign, ten si to z toho preloadu vezme
		    //TODO zatim to nefunguje na pole...
	  }
	  
	  public void visit(final MyInteger n) {
	    n.f0.accept(this);
		  
	  }	
}
